package aws;

import java.net.InetAddress;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.UnknownHostException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import pjaws.PjawsConstants;

import com.amazon.aes.webservices.client.Jec2;
import com.amazon.aes.webservices.client.KeyPairInfo;
import com.amazon.aes.webservices.client.RequestResultPair;
import com.amazon.aes.webservices.client.ReservationDescription;

/**
 * A Wrapper on Jec2 to provide abstractions for PJAWS.
 * 
 * TODO make the class provide instances as needed and not kill instances when a
 * PJAWs application is done unless an hour limit is up.
 * 
 * @author Zachary Cancio
 * 
 */

public class EC2 {
	private String myKey;
	private String myCert;
	private URL myURL;
	private Jec2 myJec2;
	private Map<String, EC2Instance> myEC2Instances;

	public EC2(String keyPath, String certPath) {
		myKey = keyPath;
		myCert = certPath;
		Initialize();
	}

	private void Initialize() {
		myEC2Instances = new HashMap<String, EC2Instance>();
		try {
			myURL = new URL("http://ec2.amazonaws.com/");
			myJec2 = new Jec2(myURL, myKey, myCert);
			// TODO automatically create key pair, save to id_rsa.pub file, and
			// ssh-add
			String[] keypairs = { "pjaws_keypair" };
			RequestResultPair<List<KeyPairInfo>> describeResults = myJec2
					.describeKeyPairs(keypairs);
			System.out.println("DescribeKeyPairs: "
					+ describeResults.getResponse());
		} catch (MalformedURLException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

	}

	public String createInstance(String imageId) {

		EC2Instance instance = null;
		try {
			RequestResultPair<ReservationDescription> runResults = myJec2
					.runInstances(imageId, 1, 1, null, null,
							PjawsConstants.KEYPAIR, null, null, null, null,
							null, null, null);
			instance = new EC2Instance(runResults.getResponse().instances
					.get(0));
			myEC2Instances.put(instance.getId(), instance);
			return instance.getId();

		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		return null;

	}

	public void terminateInstance(String id) {
		String[] ids = { id };
		try {
			myJec2.terminateInstances(ids);
			myEC2Instances.remove(id);
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

	public void terminateAllIstances() {
		String[] ids = new String[myEC2Instances.size()];
		int i = 0;
		for (EC2Instance ins : myEC2Instances.values()) {
			ids[i] = ins.getId();
			i++;
		}
		try {
			if (ids.length > 0) {
				myJec2.terminateInstances(ids);
			}
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		myEC2Instances.clear();
	}

	public String getInstanceHostnameById(String id) {
		String hostname = null;
		String instanceIDs[] = { id };
		RequestResultPair<List<ReservationDescription>> describeResults;
		try {
			describeResults = myJec2.describeInstances(instanceIDs);
			hostname = describeResults.getResponse().get(0).instances.get(0).dnsName;
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		return hostname;
	}

	/**
	 * 
	 * @return False if instance is not running or id doens't exist, True if
	 *         instance is ready
	 * @throws Exception
	 */
	public boolean isRunning(String id) throws Exception {
		if (!myEC2Instances.containsKey(id)) {
			return false;
		}
		String instanceIDs[] = { id };
		RequestResultPair<List<ReservationDescription>> describeResults = myJec2
				.describeInstances(instanceIDs);
		String state = describeResults.getResponse().get(0).instances.get(0).state;
		if (state.equals("running"))
			return true;
		return false;
	}

	/**
	 * Waits for an instance to be running. However, only tries 10000 times.
	 * 
	 * @param id
	 * @return True if the instance is running. False if the instance is not
	 *         running after 10000 tries
	 */
	public boolean waitRunning(String id) {
		try {
			System.out.print("Waiting for " + id + ":");
			int i = 0;
			while (!isRunning(id)) {
				if (i == 10000) {
					System.out.println("Error with " + id);
					return false;
				}
				if (i % 10 == 0) {
					System.out.print("#");
				}
				i++;
			}
			System.out.println(""); // end of line
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

		return true;
	}

	/**
	 * Check to make sure an address is reachable by determining if DNS name can
	 * be resolved to an IP address
	 * 
	 * @param id
	 * @return
	 */

	public boolean isHostnameReachable(String id) {
		String hostname = getInstanceHostnameById(id);
		try {
			InetAddress address = InetAddress.getByName(hostname);
			System.out.println("Name: " + address.getHostName());
			System.out.println("Addr: " + address.getHostAddress());
			// System.out.println("Reach: " + address.isReachable(3000));
		} catch (UnknownHostException e) {
			System.err.println("Unable to lookup " + hostname);
			return false;
		}
		return true;

	}

}
